package la.iok.hzsvn.lewin.movie.shopmovie.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import la.iok.hzsvn.lewin.movie.core.RoleEnum;
import la.iok.hzsvn.lewin.movie.core.Token;
import la.iok.hzsvn.lewin.movie.core.exception.ErrorCode;
import la.iok.hzsvn.lewin.movie.core.model.*;
import la.iok.hzsvn.lewin.movie.services.MovieServiceInvoker;
import la.iok.hzsvn.lewin.movie.services.OrgUnitIntegration;
import la.iok.hzsvn.lewin.movie.shopmovie.entity.*;
import la.iok.hzsvn.lewin.movie.shopmovie.mapper.CarpoolMapper;
import la.iok.hzsvn.lewin.movie.shopmovie.model.*;
import la.iok.hzsvn.lewin.mybatis.model.PageParam;
import la.iok.hzsvn.lewin.mybatis.model.PageVo;
import la.iok.hzsvn.lewin.mybatis.model.SortParam;
import la.iok.hzsvn.lewin.mybatis.service.BaseServiceImpl;
import la.iok.hzsvn.share.annotations.NotNull;
import la.iok.hzsvn.share.annotations.NotNullElement;
import la.iok.hzsvn.share.annotations.Nullable;
import la.iok.hzsvn.share.collection.IdOperators;
import la.iok.hzsvn.share.phone.PhoneNumberUtils;
import la.iok.hzsvn.share.tools.datetime.DateTimeUtil;
import la.iok.hzsvn.share.tools.exception.SystemException;
import la.iok.hzsvn.share.utils.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;

import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class CarpoolServiceImpl extends BaseServiceImpl<Carpool, CarpoolMapper, Long> implements CarpoolService {
    private final ShopMovieService shopMovieService;
    private final MovieServiceInvoker movieServiceInvoker;
    private final OrgUnitIntegration orgUnitIntegration;
    private final PlayerService playerService;
    private final ShopPlayerService shopPlayerService;
    private final PlayerFeeService playerFeeService;

    private final CouponService couponService;

    private final CouponUseRecordService couponUseRecordService;

    @Lazy
    public CarpoolServiceImpl(CarpoolMapper mapper,
                              ShopMovieService shopMovieService,
                              MovieServiceInvoker movieServiceInvoker,
                              OrgUnitIntegration orgUnitIntegration,
                              PlayerService playerService,
                              ShopPlayerService shopPlayerService,
                              PlayerFeeService playerFeeService,
                              CouponService couponService,
                              CouponUseRecordService couponUseRecordService) {
        super(mapper);
        this.shopMovieService = shopMovieService;
        this.movieServiceInvoker = movieServiceInvoker;
        this.orgUnitIntegration = orgUnitIntegration;
        this.playerService = playerService;
        this.shopPlayerService = shopPlayerService;
        this.playerFeeService = playerFeeService;
        this.couponService = couponService;
        this.couponUseRecordService = couponUseRecordService;
    }

    @Override
    public Long save(@NotNull Token token, @NotNull CarpoolParam param) {
        token.validAnyAuthority(RoleEnum.Manager, RoleEnum.ShopManager, RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(token.getOrgUnitId(), "组织","操作用户不属于任何组织");
        validateOrgUnit(token.getOrgUnitId());

        validateParam(token, param);

        associateMovieProperties(param ,true);
        Carpool entity = new Carpool();
        entity.setShopMovieId(param.getShopMovieId());
        entity.setStartTime(param.getStartTime());
        entity.setOrgUnitId(token.getOrgUnitId());
        entity.setTitle(param.getTitle());
        entity.setPlayerCount(param.getPlayerCount());
        entity.setTag(MovieBase.storeTag(param.getTag()));

        insertSelective(entity);
        return entity.getId();
    }

    @Transactional
    @Override
    public void modify(@NotNull Token token, @Nullable Long id, @NotNull CarpoolParam param) {
        token.validAnyAuthority(RoleEnum.Admin,RoleEnum.Manager, RoleEnum.ShopManager, RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(param.getVersion(),"版本","对象版本不能为空");
        Carpool entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, id);
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(entity.getVersion(),param.getVersion(),id);
        ErrorCode.PROPERTY_CHECK_ERROR.assertEqual(Carpool.STATUS_PREPARE,entity.getStatus(),"状态","只有拼车中的状态才能修改");
        if(!token.hasAuthority(RoleEnum.Admin)){
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(token.getOrgUnitId(), entity.getOrgUnitId(),entity.getOrgUnitId());
        }
        validateOrgUnit(entity.getOrgUnitId());
        validateParam(token, param);
        associateMovieProperties(param, false);
        entity = new Carpool();
        BeanUtils.copyProperties(param, entity);
        entity.setTag(MovieBase.storeTag(param.getTag()));
        entity.setId(id);
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1,mapper.updateWithVersion(entity));
        //看一下剧本费是否都已经添加,如果有未添加剧本费的,则添加剧本费
        playerFeeService.updateMovieFee(select(id));
    }

    @Override
    public PageVo<CarpoolVo> page(@NotNull Token token, @NotNull PageParam pageParam, @NotNull SortParam sortParam,
                                  @NotNull CarpoolListParam listParam) {
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(token.getUserId(), "用户","只能登录用户调用");
        Set<Long> orgIds = new HashSet<>();
        Long userId = null;
        if(token.hasAuthority(RoleEnum.Admin)){
            //nothing
        }else if(token.hasAnyAuthority(RoleEnum.Manager, RoleEnum.ShopManager, RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM)){
            orgUnitIntegration.userManageOrgUnits(token, orgIds);
            userId = token.getUserId();
        }else{
            userId = token.getUserId();
        }
        if(listParam.getStatus()!=null)
        if(listParam.getStatus()==2) {
            if (token.hasAnyAuthority(RoleEnum.ShopManager)) {
                List<UserRoleDetail> roleList = movieServiceInvoker.userRoles(token.getUserId());
                orgUnitIntegration.userManageOrgUnits(token, orgIds);
                userId = token.getUserId();
                if (roleList.stream().anyMatch(obj -> obj.getRole().equals("ShopManager"))) {
                    System.out.println();
                    System.out.println("进店长权限查询了");
                    Optional<UserRoleDetail> foundUserRoleVo = roleList.stream().filter(obj -> obj.getRole().equals("ShopManager")).findFirst();
                    if (foundUserRoleVo.isPresent()) {
                        UserRoleVo rolevo = foundUserRoleVo.get();
                        Date addTime = rolevo.getAddTime();
                        if (listParam.getStart() != null) {
                            if (addTime.after(listParam.getStart())) {
                                listParam.setStart(addTime);
                                if (listParam.getEnd() == null) {
                                    LocalDateTime currentDateTime = LocalDateTime.now();
                                    Date date = Date.from(currentDateTime.atZone(ZoneId.systemDefault()).toInstant());
                                    listParam.setEnd(date);
                                }
                            }
                        } else {
                            listParam.setStart(addTime);
                            if (listParam.getEnd() == null) {
                                LocalDateTime currentDateTime = LocalDateTime.now();
                                Date date = Date.from(currentDateTime.atZone(ZoneId.systemDefault()).toInstant());
                                listParam.setEnd(date);
                            }
                        }

                    }
                }
            }
            if(!token.hasAnyAuthority(RoleEnum.Admin,RoleEnum.Manager, RoleEnum.ShopManager))
            {
                if(listParam.getDmUserId()!=null) ErrorCode.INVALID_OPERATION.assertEqual(token.getUserId(),listParam.getDmUserId());
                if(listParam.getCsUserId()!=null) ErrorCode.INVALID_OPERATION.assertEqual(token.getUserId(),listParam.getCsUserId());
                if(listParam.getPlayerUserId()!=null) ErrorCode.INVALID_OPERATION.assertEqual(token.getUserId(),listParam.getPlayerUserId());
            }
        }



        String orderColumn = sortParam.orderColumn("status");
        String order = sortParam.order("ASC");
        Page<Carpool> list = PageHelper.startPage(pageParam.getPage(),pageParam.getLimit());
        System.out.println();
        System.out.println("实际查询时间STart= "+listParam.getStart());
        System.out.println();
        System.out.println("实际查询时间End= "+listParam.getEnd());
        System.out.println();
        System.out.println("查询参数-DmuserId="+listParam.getDmUserId()+" "+"CsUserID="+listParam.getCsUserId()+" "+"PlayerUserId="+listParam.getPlayerUserId());
        System.out.println();
        System.out.println("listParam-stataus="+listParam.getStatus());
        mapper.listByParam(order, orderColumn, listParam, orgIds, userId);
        Set<Long> orgUnitIds = list.stream().map(Carpool::getOrgUnitId).filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> shopMovieIds = list.stream().map(Carpool::getShopMovieId).filter(Objects::nonNull).collect(Collectors.toSet());
        List<SimpleOrgUnitVo> orgUnits = movieServiceInvoker.orgUnits(orgUnitIds);
        List<ShopMovie> shopMovies = shopMovieService.listInProperty("id",shopMovieIds);
        Set<Long> userIds = list.stream().flatMap(item->Stream.of(item.getBeginnerId(), item.getTerminatorId(), item.getConfirmorId())).collect(Collectors.toSet());
        List<SimpleUser> users = movieServiceInvoker.userInfo(userIds);
        List<CarpoolPlayerCount> counts = playerService.carpoolPlayerCounts(list.stream().map(Carpool::getId).collect(Collectors.toSet()));
        return page(list, item-> po2vo(item, orgUnits, shopMovies,users, counts));
    }

    @Override
    public CarpoolVo po2vo(Carpool entity) {
        if(entity == null){
            return null;
        }
        SimpleOrgUnitVo orgUnit = movieServiceInvoker.orgUnitInfo(entity.getOrgUnitId());
        ShopMovie shopMovie = shopMovieService.select(entity.getShopMovieId());
        List<SimpleUser> users = movieServiceInvoker.userInfo(Arrays.asList(entity.getBeginnerId(), entity.getTerminatorId()));
        List<CarpoolPlayerCount> counts = playerService.carpoolPlayerCounts(Collections.singleton(entity.getId()));
        return po2vo(entity, shopMovie, orgUnit,users,counts);
    }



    @Transactional
    @Override
    public List<JoinPlayerResult> joinPlayer(@NotNull Token token, @Nullable Long id, @NotNull JoinPlayerParam param) {
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotEmpty(param.getUserIds(),"玩家","玩家不能为空,请添加玩家后重试");
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(token.getUserId(),"操作者","操作者只能是用户");
        token.validAnyAuthority(RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
        Carpool entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, id);

        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), token.getOrgUnitId(),entity.getOrgUnitId());
        ErrorCode.OPERATION_TYPE_ERROR.assertNotEquals(entity.getStatus(),2);
        if(entity.getPlayerCount() != null && entity.getPlayerCount() > 0){
            List<Player> carpoolPlayers = playerService.players(id);
            ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(carpoolPlayers.size() + param.getUserIds().size() <= entity.getPlayerCount(),()->new Object[]{
                    "玩家",String.format("玩家数量[%s]超过最大人数标准[%s]",carpoolPlayers.size() + param.getUserIds().size(), entity.getPlayerCount())
            });
        }
        List<JoinPlayerResult> results;
        if(param.virtualPlayer()){
            results = new ArrayList<>();
            long saveUserId = playerService.players(id).stream().map(Player::getUserId).filter(Objects::nonNull).min(Long::compareTo).orElse(0L);
            saveUserId = saveUserId <= 0? saveUserId-1:-1L;
            for(Long ignored : param.getUserIds()){
                results.add(joinVirtualPlayer(entity, saveUserId--));
            }
        }else{
            List<SimpleUser> users = movieServiceInvoker.userInfo(param.getUserIds());
            List<Carpool> list = of(token.getOrgUnitId());
            List<Player> players = playerService.listOfCarpoolUsers(list.stream().map(Carpool::getId).collect(Collectors.toSet()),
                    users.stream().map(SimpleUser::getId).collect(Collectors.toSet()));
            results = param.getUserIds().stream().map(userId->{
                SimpleUser user = IdOperators.find(users, userId);
                if(user != null){
                    return joinCarpool(token.getUserId(), entity, param.confirmJoin(), list, players, user);
                }else{
                    //用户未找到
                    return JoinPlayerResult.failed(userId, "用户不存在");
                }
            }).collect(Collectors.toList());
        }
        playerFeeService.updateMovieFee(entity);
        return results;
    }

    private JoinPlayerResult joinCarpool(Long guideId, Carpool entity, boolean confirm, List<Carpool> list, List<Player> players, SimpleUser user) {
        List<Player> player = players.stream().filter(item->Objects.equals(item.getUserId(), user.getId())).collect(Collectors.toList());
        ErrorCode.OPERATION_TYPE_ERROR.assertNotEquals(entity.getStatus(),2);
        if(!player.isEmpty()){
            Player sameCarpoolOne = player.stream().filter(item->Objects.equals(entity.getId(), item.getCarpoolId())).findFirst().orElse(null);
            if(sameCarpoolOne !=null){
                //已经加入当前车了
                return JoinPlayerResult.failed(user.getId(),"玩家已经在当前拼车中");
            }else{
                //已经在拼车了
                if(confirm){
                    playerService.save(user.getId(), entity.getId(), guideId);
                    shopPlayerService.save(user,entity.getOrgUnitId());
                    return JoinPlayerResult.success(user.getId());
                }else{
                    Carpool carpool = IdOperators.find(list, player.get(0).getCarpoolId());
                    String message = String.format("用户正在[%s]的拼车中", carpool.getTitle());
                    return JoinPlayerResult.confirm(user.getId(),message);
                }
            }
        }else{
            playerService.save(user.getId(), entity.getId(), guideId);
            shopPlayerService.save(user, entity.getOrgUnitId());
            return JoinPlayerResult.success(user.getId());
        }
    }

    /**
     * 保存虚拟玩家。虚拟玩家在保存的时候就已经确认收费
     * @param entity 拼车对象
     * @return 添加结果
     */
    private JoinPlayerResult joinVirtualPlayer(Carpool entity,Long userId){
        playerService.saveVirtualPlayer(entity.getId(), userId);
        return JoinPlayerResult.success(userId);
    }

    @Transactional
    @Override
    public JoinPlayerResult joinPlayer(@NotNull Token token, @NotNull PlayerJoinCarpoolParam param) {
        /**查询是否存在该拼车**/
        Carpool entity = select(param.getCarpoolId());
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, param.getCarpoolId());

        List<SimpleUser> users = movieServiceInvoker.userInfo(Arrays.asList(token.getUserId(), param.getGuideId()));
        SimpleUser guide = IdOperators.find(users, param.getGuideId());
        ErrorCode.INVALID_USER.assertNotNull(guide);
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), guide.getOrgUnitId(), entity.getOrgUnitId());
        ErrorCode.INVALID_USER.assertTrue(guide.normalStatus());
        SimpleUser player = IdOperators.find(users, token.getUserId());
        ErrorCode.INVALID_USER.assertNotNull(player);
        ErrorCode.INVALID_USER.assertTrue(player.normalStatus());
        List<Carpool> list = of(guide.getOrgUnitId());
        List<Player> players = playerService.listOfCarpoolUsers(list.stream().map(Carpool::getId).collect(Collectors.toSet()),
                Collections.singleton(token.getUserId()));
        JoinPlayerResult result = joinCarpool(param.getGuideId(), entity, param.confirmJoin(), list, players, player);
        playerFeeService.updateMovieFee(entity);
        updateRecordId(entity.getOrgUnitId(),token.getUserId());
        return result;
    }

    @Override
    public void updateRemark(Token token, Long playerId, String remark) {
        token.validAnyAuthority(RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
        Player player = playerService.select(playerId);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(player, playerId);
        Carpool entity = select(player.getCarpoolId());
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(entity, ()->new Object[]{"拼车","玩家所属拼车[" + player.getCarpoolId() + "]不存在"});
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), token.getOrgUnitId(),entity.getOrgUnitId());
        playerService.updateRemark(playerId, remark);
    }

    @Override
    public Long selectRecordId(Long userId) {
        return mapper.selectRecorId(userId);
    }

    @Override
    public int updateRecordId(Long orgId, Long userId) {
        return mapper.updateRecordId(orgId,userId);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<PlayerVo> players(@NotNull Token token, @Nullable Long id) {
        Carpool entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, id);
        List<Player> list = playerService.players(id);
        Player item = list.stream().filter(player -> Objects.equals(player.getUserId(), token.getUserId())).findFirst().orElse(null);
        if(item == null){
            // 不是拼车中的玩家，那就要有拼车的查看权限
            token.validAnyAuthority(RoleEnum.Admin, RoleEnum.Manager, RoleEnum.ShopManager, RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
            if(!token.hasAuthority(RoleEnum.Admin)){
                ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), token.getOrgUnitId(), entity.getOrgUnitId());
            }
        }
        return playerService.po2vo(entity, list);
    }

    @Transactional
    @Override
    public void fees(@NotNull Token token, @Nullable Long id, @NotNull PlayerFeesParam param) {
        token.validAnyAuthority(RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotEmpty(param.getPlayers(),"玩家","请选择玩家");
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotEmpty(param.getFees(),"收费项","收费项不能为空");
        Carpool entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity,id);
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), token.getOrgUnitId(), entity.getOrgUnitId());
        List<Player> players = playerService.listInProperty("id", param.getPlayers());
        ErrorCode.PROPERTY_CHECK_ERROR.assertEqual(players.size(), param.getPlayers().size(),()->{
            Set<Long> exist = players.stream().map(Player::getId).collect(Collectors.toSet());
            String notExist = param.getPlayers().stream().filter(item->!exist.contains(item)).map(String::valueOf).collect(Collectors.joining(","));
            return new Object[]{"玩家",String.format("玩家[%s]不存在",notExist)};
        });
        Set<Long> carpoolIds = players.stream().map(Player::getCarpoolId).collect(Collectors.toSet());
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(carpoolIds.contains(id),"玩家","只能选择加入拼车的玩家");
        ErrorCode.PROPERTY_CHECK_ERROR.assertEqual(1, carpoolIds.size(),()->{
            String otherCarpoolPlayerIds = players.stream().filter(player -> !Objects.equals(player.getCarpoolId(),id)).map(Player::getId).map(String::valueOf).collect(Collectors.joining(","));
            return new Object[]{"玩家",String.format("玩家[%s]不属于当前拼车",otherCarpoolPlayerIds)};
        });

        for(FeesParam fees : param.getFees()){
            if(fees == null){//真有这种情况那也不管了
                continue;
            }
            players.forEach(player -> playerFeeService.saveAdditionalFee(player, fees.getName(),fees.getAmount()));
        }
    }

    @Override
    @NotNull
    @NotNullElement
    public List<PlayerFee> fees(@NotNull Token token, @Nullable Long id) {
        Carpool entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, id);
        List<Player> list = playerService.players(id);
        Player item = list.stream().filter(player -> Objects.equals(player.getUserId(), token.getUserId())).findFirst().orElse(null);
        if(item == null){
            // 不是拼车中的玩家，那就要有拼车的查看权限
            token.validAnyAuthority(RoleEnum.Admin, RoleEnum.Manager, RoleEnum.ShopManager, RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
            if(!token.hasAuthority(RoleEnum.Admin)){
                ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), token.getOrgUnitId(), entity.getOrgUnitId());
            }
        }
        Set<Long> players = list.stream().map(Player::getId).collect(Collectors.toSet());
        return playerFeeService.listInProperty("playerId", players, "feeType");
    }

    @Override
    public void confirmPay(Token token, PlayerFeePayParam param) {
        PlayerFee playerFee = playerFeeService.select(param.getId());
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(playerFee, param.getId());
        Player player = playerService.select(playerFee.getPlayerId());
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(player, playerFee.getPlayerId());
        if(Objects.equals(token.getUserId(), player.getUserId())){
            //玩家自己支付
            if(Objects.equals(param.getPayType(),PlayerFee.PAY_TYPE_VIP)){
                throw new SystemException("暂时未支持");
            }
            throw new SystemException("只能使用VIP支付");
        }else{
            //客服、DM等确认用户已经付款
            token.validAnyAuthority(RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
            Carpool carpool = select(player.getCarpoolId());
            ErrorCode.RECORD_NOT_EXIST.assertNotNull(carpool, player.getCarpoolId());
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(carpool.getOrgUnitId(), token.getOrgUnitId(), carpool.getOrgUnitId());
            ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(playerFee.getVersion(), param.getVersion(), param.getId());
            playerFee.setPayConfirmorId(token.getUserId());
            playerFee.setPayConfirmTime(new Date());
            playerFee.setStatus(PlayerFee.STATUS_PAID);
            playerFee.setPayType(param.getPayType());
            if(playerFee.getCouponDeductionAmount()!=0)
            {
                CouponUseRecord couponUseRecord=couponUseRecordService.selectCouponUseRecord(carpool.getId(),playerFee.getId());
                ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(couponUseRecord,"优惠券","未查找到该优惠券使用记录");
                Coupon coupon=couponService.select(couponUseRecord.getCouponId());
                ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1,couponService.lock(coupon),coupon.getId());
            }
            ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1, playerFeeService.confirmPaid(playerFee),param.getId());
        }
    }

    @Transactional
    @Override
    public void cancelPayment(Token token, Long playerFeeId, Long version) {
        //客服、拼车管理员等确认用户已经付款
        token.validAnyAuthority(RoleEnum.CarpoolMaster, RoleEnum.CustomerService);
        PlayerFee playerFee = playerFeeService.select(playerFeeId);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(playerFee, playerFeeId);
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(playerFee.paid(),"支付状态","未支付");
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(playerFee.getVersion(), version, playerFeeId);
        Player player = playerService.select(playerFee.getPlayerId());
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(player, playerFee.getPlayerId());
        Carpool carpool = select(player.getCarpoolId());
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(carpool, player.getCarpoolId());
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(carpool.getOrgUnitId(), token.getOrgUnitId(), carpool.getOrgUnitId());
        ErrorCode.PROPERTY_CHECK_ERROR.assertEqual(Carpool.STATUS_PREPARE, carpool.getStatus(),"状态","只有拼车中状态才能取消");
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1, playerFeeService.cancelPayment(playerFee),playerFeeId);


        if(playerFee.getCouponDeductionAmount()!=0) {
            CouponUseRecord couponUseRecord=couponUseRecordService.selectCouponUseRecord(player.getCarpoolId(),playerFeeId);
            ErrorCode.RECORD_NOT_EXIST.assertNotNull(couponUseRecord, "优惠券", "未找到优惠券使用记录");
            Coupon coupon = couponService.select(couponUseRecord.getCouponId());
            ErrorCode.RECORD_NOT_EXIST.assertNotNull(coupon, "优惠券", "未找到优惠券记录");
            ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1, couponService.unLock(coupon), playerFeeId);
        }


    }

    @Override
    public void start(@NotNull Token token, Long id) {
        token.validAnyAuthority(RoleEnum.DM);
        Carpool carpool = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(carpool, id);
        ErrorCode.PROPERTY_CHECK_ERROR.assertEqual(Carpool.STATUS_PREPARE, carpool.getStatus(),"状态","只有拼车中状态才可以发车");
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(carpool.getOrgUnitId(), token.getOrgUnitId(), carpool.getOrgUnitId());
        ShopMovie shopMovie = shopMovieService.select(carpool.getShopMovieId());
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(shopMovie, "剧本","店铺剧本不存在");
        List<Player> players = playerService.players(id);
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(players.size() >= shopMovie.getMinPlayers(),()->new Object[]{
                "玩家",String.format("玩家数量[%s]未达到最低人数标准[%s]",players.size(), shopMovie.getMinPlayers())});
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(players.size() <= shopMovie.getMaxPlayers(),()->new Object[]{
                "玩家",String.format("玩家数量[%s]超过最大人数标准[%s]",players.size(), shopMovie.getMaxPlayers())
        });
        List<PlayerFee> playerFees = playerFeeService.listInProperty("playerId",players.stream().map(Player::getId).collect(Collectors.toList()));
        List<PlayerFee> notPaid = playerFees.stream().filter(item->!item.paid()).collect(Collectors.toList());
        ErrorCode.PROPERTY_CHECK_ERROR.assertEmpty(notPaid,()->{
            Set<Long> notPaidPlayers = notPaid.stream().map(PlayerFee::getPlayerId).collect(Collectors.toSet());
            Set<Long> userIds = players.stream().filter(player -> notPaidPlayers.contains(player.getId())).map(Player::getUserId).collect(Collectors.toSet());
            return new Object[]{"费用",String.format("尚有%s玩家未支付费用",userIds.size())};
        });
        for (PlayerFee playerFee:playerFees
             ) {
            if(playerFee.getCouponDeductionAmount()!=0)
            {
                CouponUseRecord couponUseRecord=couponUseRecordService.selectCouponUseRecord(id,playerFee.getId());
                ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(couponUseRecord, "优惠券","未查询到优惠券使用记录");
                Coupon coupon=couponService.select(couponUseRecord.getCouponId());
                ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(coupon, "优惠券","未查询到优惠券");
             //   ErrorCode.PROPERTY_CHECK_ERROR.assertEqual(5, coupon.getStatus(),"优惠券状态","只有锁定的券才能使用");
                ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1, couponService.use(coupon), coupon.getId());
            }


        }
        carpool.setStatus(Carpool.STATUS_GAMING);
        carpool.setBeginnerId(token.getUserId());
        carpool.setBeginTime(new Date());
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1,mapper.updateBegin(carpool));
        //增加店铺剧本的开本次数
        shopMovieService.increment("playTimes", carpool.getShopMovieId());
    }

    @Override
    public void end(@NotNull Token token, Long id) {
        token.validAnyAuthority(RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
        Carpool carpool = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(carpool, id);
        ErrorCode.PROPERTY_CHECK_ERROR.assertEqual(Carpool.STATUS_GAMING, carpool.getStatus(),"状态","只有已发车状态才可以结束");
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(carpool.getOrgUnitId(), token.getOrgUnitId(), carpool.getOrgUnitId());
        carpool.setStatus(Carpool.STATUS_ENDED);
        carpool.setTerminatorId(token.getUserId());
        carpool.setTerminateTime(new Date());
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1,mapper.updateEnd(carpool));
    }

    @Override
    public void confirm(Token token, Long id) {
        token.validAnyAuthority(RoleEnum.Manager, RoleEnum.ShopManager, RoleEnum.CarpoolMaster);
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(token.getUserId(),"用户id","登录用户的id不能为null");
        Carpool carpool = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(carpool, id);
        ErrorCode.PROPERTY_CHECK_ERROR.assertAnyOf(carpool.getStatus(),new Object[]{Carpool.STATUS_GAMING, Carpool.STATUS_ENDED},"状态","必须是已发车或者已结束状态");
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(carpool.getOrgUnitId(), token.getOrgUnitId(), carpool.getOrgUnitId());
        carpool.setConfirmorId(token.getUserId());
        carpool.setConfirmTime(new Date());
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1, mapper.confirm(carpool), id);
    }

    @Override
    public void deleteFee(@NotNull Token token, Long id) {
        token.validAnyAuthority(RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
        PlayerFee playerFee = playerFeeService.select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(playerFee, id);
        Player player = playerService.select(playerFee.getPlayerId());
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(player, "玩家", "玩家["+playerFee.getPlayerId()+"]不存在,请刷新重试");
        if(!player.virtualType()){
            ErrorCode.PROPERTY_CHECK_ERROR.assertEqual(PlayerFee.STATUS_UNPAID, playerFee.getStatus(),"状态","只有未支付状态才能删除");
            ErrorCode.PROPERTY_CHECK_ERROR.assertNotEquals(PlayerFee.FEE_TYPE_MOVIE, playerFee.getFeeType(),"类型","剧本费不能删除");
        }
        Carpool entity = select(player.getCarpoolId());
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(entity, "拼车","拼车[" + player.getCarpoolId() + "]不存在");
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), token.getOrgUnitId(), entity.getOrgUnitId());
        playerFeeService.delete(id);
    }

    @Override
    public void delete(Token token, Long id) {
        token.validAnyAuthority(RoleEnum.CarpoolMaster, RoleEnum.CustomerService, RoleEnum.DM);
        Carpool entity = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(entity, id);
        ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(entity.getOrgUnitId(), token.getOrgUnitId(),entity.getOrgUnitId());
        List<Player> players = playerService.players(id);
        ErrorCode.PROPERTY_CHECK_ERROR.assertEmpty(players, ()-> new Object[]{"玩家", "拼车存在[" + players.size() +"]个玩家,不能删除"});
        delete(id);
    }

    @NotNull
    @Override
    public CarpoolReport dmCarpoolReport(@NotNull Token token, @NotNull CarpoolReport report) {
        Collection<Long> orgUnitIds = null;
        if(token.hasAuthority(RoleEnum.Admin)){
            //nothing
        }else if(token.hasAnyAuthority(RoleEnum.Manager, RoleEnum.ShopManager, RoleEnum.CarpoolMaster, RoleEnum.DM)){
            orgUnitIds = new HashSet<>();
            orgUnitIntegration.userManageOrgUnits(token, orgUnitIds);
        }else{
            return report;
        }
        List<CarpoolReportCount> list = mapper.dmCarpoolReport(orgUnitIds, report);
        List<SimpleOrgUnitVo> orgUnits = movieServiceInvoker.orgUnits(list.stream().map(CarpoolReportCount::getOrgUnitId).collect(Collectors.toSet()));
        List<SimpleUser> users = movieServiceInvoker.userInfo(list.stream().map(CarpoolReportCount::getUserId).collect(Collectors.toSet()));
        report.fillRecords(list,orgUnits, users);
        return report;
    }

    /**
     * 查询店铺中正在拼车和正在游戏中的拼车
     * @param orgUnitId 店铺id
     * @return 正在拼车和正在游戏中的拼车列表
     */
    @NotNull
    @NotNullElement
    private List<Carpool> of(Long orgUnitId){
        if(orgUnitId == null){
            return Collections.emptyList();
        }
        return mapper.listByWhere("orgUnitId=" + orgUnitId + " and status in ("+Carpool.STATUS_PREPARE + ","+Carpool.STATUS_GAMING+")");
    }

    /**
     * 根据剧本修改玩家数量和标签。如果没有选择剧本,则不修改标签和玩家数量。如果标题为空则计算标题
     * @param param 拼车实体对象
     */
    private void associateMovieProperties(@NotNull CarpoolParam param, boolean add) {
        String movieName = null;
        if(param.getShopMovieId() != null) {
            ShopMovie shopMovie = shopMovieService.select(param.getShopMovieId()); //已经验证过了，肯定是存在的
            if(StringUtils.isNotBlank(shopMovie.getTag())){
                param.setTag(MovieBase.showTag(shopMovie.getTag()));
            }
            if(shopMovie.getMaxPlayers() != null && shopMovie.getMaxPlayers() > 0){
                param.setPlayerCount(shopMovie.getMaxPlayers());
            }
            movieName = shopMovie.getMovieName();
        }
        if(StringUtils.isBlank(param.getTitle()) && add){  //如果是修改,不自动改变title,除非用户明确指定title,此时title非空，直接使用指定的title
            SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm");
            String time = null;
            if(param.getStartTime() != null){
                time = sdf.format(param.getStartTime());
            }
            String title = Stream.of(time, movieName, MovieBase.storeTag(param.getTag())).filter(StringUtils::isNotBlank).collect(Collectors.joining("-"));
          //  param.setTitle(title);
        }
    }

    private void validateOrgUnit(Long orgUnitId) {
        SimpleOrgUnitVo orgUnit = movieServiceInvoker.orgUnitInfo(orgUnitId);
        ErrorCode.ORG_UNIT_NOT_EXIST.assertNotNull(orgUnit, orgUnitId);
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(orgUnit.shopType(),"店铺","只允许店铺进行操作");
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(orgUnit.normalStatus(),"店铺","当前店铺已经下线");
    }

    private CarpoolVo po2vo(Carpool item, List<SimpleOrgUnitVo> orgUnits, List<ShopMovie> shopMovies, List<SimpleUser> users, List<CarpoolPlayerCount> counts) {
        ShopMovie shopMovie = IdOperators.find(shopMovies, item.getShopMovieId());
        SimpleOrgUnitVo orgUnit = IdOperators.find(orgUnits, item.getOrgUnitId());
        return po2vo(item, shopMovie, orgUnit,users,counts);
    }

    private CarpoolVo po2vo(Carpool item, ShopMovie shopMovie, SimpleOrgUnitVo orgUnit,List<SimpleUser> users, List<CarpoolPlayerCount> counts) {
        CarpoolVo vo = new CarpoolVo();
        BeanUtils.copyProperties(item, vo);
        vo.setTag(MovieBase.showTag(item.getTag()));
        if(shopMovie != null){
            vo.setMovieId(shopMovie.getMovieId());
            vo.setMovieName(shopMovie.getMovieName());
            vo.setMovieVersion(shopMovie.getMovieVersion());
            vo.setMinPlayerCount(shopMovie.getMinPlayers());
        }
        if(orgUnit != null){
            vo.setOrgUnitName(orgUnit.getName());
        }
        SimpleUser user = IdOperators.find(users, item.getBeginnerId());
        if(user != null){
            vo.setBeginnerNickname(user.getNickname());
            vo.setBeginnerPhoneNumber(PhoneNumberUtils.encPhoneNumber(user.getPhoneNumber()));
        }
        user = IdOperators.find(users, item.getTerminatorId());
        if(user != null){
            vo.setTerminatorNickname(user.getNickname());
            vo.setTerminatorPhoneNumber(PhoneNumberUtils.encPhoneNumber(user.getPhoneNumber()));
        }else{
            if(Objects.equals(item.getTerminatorId(), -1L)){
                vo.setTerminatorNickname("系统");
            }
        }
        user = IdOperators.find(users, item.getConfirmorId());
        if(user != null){
            vo.setConfirmorNickname(user.getNickname());
            vo.setConfirmorPhoneNumber(PhoneNumberUtils.encPhoneNumber(user.getPhoneNumber()));
        }
        CarpoolPlayerCount count = IdOperators.find(counts, item.getId());
        if(count!= null){
            vo.setJoinedPlayerCount(count.getCount());
        }else{
            vo.setJoinedPlayerCount(0);
        }
        return vo;
    }

    private void validateParam(Token token, CarpoolParam param) {
        if(param.getShopMovieId() != null){
            ShopMovie shopMovie = shopMovieService.select(param.getShopMovieId());
            ErrorCode.RECORD_NOT_EXIST.assertNotNull(shopMovie, param.getShopMovieId());
            ErrorCode.MOVIE_OFFLINE.assertEqual(MovieVo.STATUS_ONLINE, shopMovie.getStatus(),shopMovie.getMovieName());
            ErrorCode.MOVIE_ACCESS_DENIED.assertEqual(shopMovie.getOrgUnitId(), token.getOrgUnitId(),shopMovie.getMovieName());
        }

        if(param.getStartTime() != null){
            // 时间从昨天到
            Date now = new Date();
            Date yesterday = DateTimeUtil.setTime(DateTimeUtil.preDate(now),0,0,0,0);
            Date oneMonth = DateTimeUtil.setTime(DateTimeUtil.nextMonth(now),23,59,59,999);
            ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(param.getStartTime().after(yesterday),"时间","开车时间必须晚于昨天");
            ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(param.getStartTime().before(oneMonth),"时间","开车时间不能超过1个月");
        }
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(param.getStartTime() != null || param.getShopMovieId() != null, "参数","开车时间和剧本不能都为空");
    }
}
